/*
 * @Author: 火红色祥云 809880192@qq.com
 * @Date: 2022-06-14 21:52:13
 * @LastEditors: 火红色祥云 809880192@qq.com
 * @LastEditTime: 2022-09-27 21:47:04
 * @FilePath: /imx6ull-app/02_led/chardevled.c
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>

#define CHARDEVLED_MAJOR 200
#define CHARDEVLED_NAME "name_chardevled"

#define LED_ON 1
#define LED_OFF 0

#define CCM_CCGR1_BASE 0x020C406C
#define SW_MUX_GPIO1_IO03_BASE 0x020E0068
#define SW_PAD_GPIO1_IO03_BASE 0x020E02F4
#define GPIO1_DR_BASE 0x0209C000
#define GPIO1_GDIR_BASE 0x0209C004

static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

/**
 * @description:
 * @param {u8} state
 * @return {*}
 */

void SwitchLED(u8 state)
{
    u32 reg_val;

    if (state == LED_ON)
    {
        reg_val = readl(GPIO1_DR);
        reg_val &= ~(1 << 3);
        writel(reg_val, GPIO1_DR);
    }
    else if (state == LED_OFF)
    {
        reg_val = readl(GPIO1_DR);
        reg_val |= (1 << 3);
        writel(reg_val, GPIO1_DR);
    }
}

int led_open(struct inode *nd, struct file *fp)
{
    return 0;
}

ssize_t led_read(struct file *fp, char __user *buf, size_t len, loff_t *offset)
{
    return 0;
}

ssize_t led_write(struct file *fp, const char __user *buf, size_t len, loff_t *offset)
{

    int retval = 0;
    unsigned char recvbuf[1];
    unsigned char led_state;
    retval = copy_from_user(recvbuf, buf, len);
    if (retval < 0)
    {
        printk("kernel write failed\r\n");
        return -EFAULT;
    }

    led_state = recvbuf[0];

    if (led_state == LED_ON)
    {
        SwitchLED(LED_ON);
    }
    else if (led_state == LED_OFF)
    {
        SwitchLED(LED_OFF);
    }

    return 0;
}

int led_release(struct inode *nd, struct file *fp)
{
    return 0;
}

struct file_operations led_fops =
    {
        .owner = THIS_MODULE,
        .open = led_open,
        .read = led_read,
        .write = led_write,
        .release = led_release,
};

static int __init chardevled_init(void)
{
    u32 reg_val;
    int ret_val;
    //寄存器地址映射
    IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
    SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
    SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
    GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
    GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);

    //使能GPIO1时钟
    reg_val = readl(IMX6U_CCM_CCGR1);
    reg_val &= ~(3 << 26);
    reg_val |= (3 << 26);
    writel(reg_val, IMX6U_CCM_CCGR1);

    //设置复用功能为GPIO
    writel(5, SW_MUX_GPIO1_IO03);

    //设置IO 属性
    writel(0x10B0, SW_PAD_GPIO1_IO03);

    //设置为输出功能
    reg_val = readl(GPIO1_GDIR);
    reg_val &= ~(1 << 3);
    reg_val |= (1 << 3);
    writel(reg_val, GPIO1_GDIR);

    //默认关闭GPIO
    reg_val = readl(GPIO1_DR);
    reg_val |= (1 << 3);
    writel(reg_val, GPIO1_DR);

    ret_val = register_chrdev(CHARDEVLED_MAJOR, CHARDEVLED_NAME, &led_fops);
    if (ret_val < 0)
    {
        printk("register chrdev failed\r\n");
        return -EIO;
    }

    return 0;
}

static void __exit chardevled_exit(void)
{

    iounmap(IMX6U_CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);

    unregister_chrdev(CHARDEVLED_MAJOR, CHARDEVLED_NAME);
}

module_init(chardevled_init);
module_exit(chardevled_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lux");